using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    #region Class Variables
    [Header("Movement")]
    private float moveSpeed;
    [SerializeField] private float walkSpeed;
    [SerializeField] private float sprintSpeed;
    [SerializeField] private float groundDrag;
    [SerializeField] private PlayerState playerState;
    [SerializeField] private Rigidbody rb;
    [SerializeField] private float movingThreshold = 0.01f;
    [SerializeField] public float stamina = 100f;

    [Header("Ground Check")]
    [SerializeField] private float playerHeight;
    [SerializeField] private LayerMask groundMask;
    [HideInInspector] public bool isGrounded;

    [Header("Keybinds")]
    public KeyCode sprintKey = KeyCode.LeftShift;

    [SerializeField] private Transform orientation;

    [HideInInspector] public float horizontalInput;
    [HideInInspector] public float verticalInput;

    private Vector3 moveDirection;
    #endregion
    #region Startup
    private void Start()
    {
        rb.freezeRotation = true;
    }
    #endregion
    #region Update Logic
    private void Update()
    {
        GroundCheck();
        GetInput();
        UpdateMovementState();
        UpdateDrag();
    }

    private void FixedUpdate()
    {
        StaminaCheck();
        MovePlayer();
        SpeedClamp();
    }

    private void UpdateMovementState()
    {
        bool isMovementInput = horizontalInput != 0f && verticalInput != 0;
        bool isMovingLaterally = IsMovingLaterally();

        PlayerMovementState lateralState = isMovingLaterally || isMovementInput ? PlayerMovementState.Running : PlayerMovementState.Idling;
        playerState.SetPlayerMovementState(lateralState);

        moveSpeed = walkSpeed;

        if (isGrounded && Input.GetKey(sprintKey) && stamina > 0)
        {
            playerState.SetPlayerMovementState(PlayerMovementState.Sprinting);
            moveSpeed = sprintSpeed;
        }

        if (!isGrounded)
        {
            playerState.SetPlayerMovementState(PlayerMovementState.Falling);
        }
    }

    private void StaminaCheck()
    {
        if(playerState.CurrentPlayerMovementState == PlayerMovementState.Sprinting)
        {
            stamina -= 0.5f;
        } 
        else if(playerState.CurrentPlayerMovementState != PlayerMovementState.Sprinting)
        {
            stamina += 0.5f;
        }

        if (stamina < 0f) stamina = 0f;
        else if (stamina > 100f) stamina = 100f;
    }

    private void GetInput()
    {
        horizontalInput = Input.GetAxisRaw("Horizontal");
        verticalInput = Input.GetAxisRaw("Vertical");
    }

    private void MovePlayer()
    {
        if(isGrounded)
        {
            moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput;
            rb.AddForce(moveDirection.normalized * moveSpeed * 10f, ForceMode.Force);

            if (rb.velocity.y < 0f)
            {
                rb.velocity.Set(rb.velocity.x, 0, rb.velocity.z);
            }
        }
    }

    private void SpeedClamp()
    {
        if(isGrounded)
        {
            Vector3 flatVel = new Vector3(rb.velocity.x, 0f, rb.velocity.z);

            if (flatVel.magnitude > moveSpeed)
            {
                Vector3 limitedVel = flatVel.normalized * moveSpeed;
                rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
            }
        }
    }

    private void GroundCheck()
    {
        isGrounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.05f, groundMask);
    }

    private void UpdateDrag()
    {
        if (isGrounded)
        {
            rb.drag = groundDrag;
        }
        else
        {
            rb.drag = 0;
        }
    }
    #endregion
    #region State Checks
    private bool IsMovingLaterally()
    {
        Vector3 lateralVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.y);

        return lateralVelocity.magnitude > movingThreshold;
    }
    #endregion
}
